#include "Channel.h"
#include "EventLoop.h"
#include "Logger.h"

#include <sys/epoll.h>

const int Channel::kNoneEvent = 0;  // 对没有任何事件感兴趣
const int Channel::kReadEvent = EPOLLIN | EPOLLPRI;  // 对读事件感兴趣(普通读、外带读)
const int Channel::kWriteEvent = EPOLLOUT;  // 对写事件感兴趣

// EventLoop: ChannelList Poller
Channel::Channel(EventLoop* loop, int fd)
    : loop_(loop)
    , fd_(fd)
    , events_(0)  // fd感兴趣的事件
    , revents_(0)  // poller通知的事件
    , index_(-1)
    , tied_(false)
{
}

Channel::~Channel()
{
}

/**
 * Channel的tie方法什么时候调用过
 * TcpConnection::connectEstablished() 一个TcpConnection新连接创建的时候调用
 * 让channel观察上层的TcpConnection对象
*/ 
void Channel::tie(const std::shared_ptr<void>& obj)
{
    tie_ = obj;  // tie_是弱智能指针，观察一个强智能指针
    tied_ = true;  // 绑定过就置为true
}

/**
 * 当改变channel所表示的fd感兴趣的events事件后，update负责在poller里更改fd相应的事件epoll_ctl
 * EventLoop 包含 ChannelLists 和 Poller
 * ChannelLists 和 Poller是相互独立的
 * Channel本身是无法向Poller注册事件的，但是可以通过EventLoop完成
 */
void Channel::update()
{
    // 通过channel所属的EventLoop，调用poller的相应方法，注册fd的events事件
    loop_->updateChannel(this);  // 通过EventLoop访问poller，修改fd的事件
}

// 在channel所属的EventLoop中把当前的Channel删除；EventLoop中有一个存放channel*的vector
void Channel::remove()
{
    loop_->removeChannel(this);
}

// fd得到poller通知以后，处理事件的
void Channel::handleEvent(Timestamp receiveTime)
{
    if(tied_)  // tie()方法中会对tied_赋值为true
    {
        std::shared_ptr<void> guard = tie_.lock();// 弱智能指针提升为强智能指针
        if(guard)  // 上层的TcpConnection对象是否存活，存活就继续处理，TcpConnection没了就不做任何处理
        {
            handleEventWithGuard(receiveTime);
        }
    }
    else
    {
        handleEventWithGuard(receiveTime);
    }
}

// 根据poller通知的channel发生的具体事件，由channel负责执行相应的回调
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
    LOG_INFO("channel handleEvent revents: %d\n", revents_);

    if((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN))  // EPOLLHUP表示对端关闭连接或发生了挂起事件，EPOLLIN表示文件描述符可读
    {
        // 一方关闭连接，且不文件描述符不可读（没有新数据可读）
        if(closeCallback_)
            closeCallback_();  // 这里调用的回调是TcpConnection::handleClose()
    }

    if(revents_ & EPOLLERR)  // 发生错误，执行错误回调
    {
        if(errorCallback_)
            errorCallback_();
    }

    if(revents_ & (EPOLLIN | EPOLLPRI))  // 读事件
    {
        if(readCallback_)
            readCallback_(receiveTime);
    }

    if(revents_ & EPOLLOUT)  // 写事件
    {
        if(writeCallback_)
            writeCallback_();
    }
}